package com.sfdk.graphics.engine.data.layers
{
    import com.sfdk.graphics.engine.api.IMapObject;
    import com.sfdk.graphics.engine.api.ITileset;
    import com.sfdk.graphics.engine.api.IWalkableLayerInformation;
    import com.sfdk.graphics.engine.core.enums.LayerType;
    import com.sfdk.graphics.engine.data.TileInfo;
    import com.sfdk.log.Logger;
    import com.sfdk.utils.Base64;
    import com.sfdk.utils.IntPoint;

    import flash.utils.ByteArray;

    public class WalkableLayer extends Layer implements IWalkableLayerInformation
    {
        //--------------------------------------------------------------------------
        //
        //  constructor
        //
        //--------------------------------------------------------------------------

        public function WalkableLayer(layerData:XML, associatedMap:IMapObject, layerType:LayerType) {
            super(layerData, associatedMap, layerType);
        }

        //--------------------------------------------------------------------------
        //
        //  properties
        //
        //--------------------------------------------------------------------------

        //----------------------------------
        // walkableData
        //----------------------------------

        /**
         * @private
         */
        protected var _walkableData:Vector.<Vector.<int>>;

        public function get walkableData():Vector.<Vector.<int>> {
            return _walkableData;
        }

        //----------------------------------
        // tmpWalkableData
        //----------------------------------

        /**
         * @private
         */
        protected var _tmpWalkableData:Vector.<Vector.<int>>;

        public function get tmpWalkableData():Vector.<Vector.<int>> {
            return _tmpWalkableData;
        }

        //--------------------------------------------------------------------------
        //
        //  methods
        //
        //--------------------------------------------------------------------------

        //----------------------------------
        // parseLayerData
        //----------------------------------

        override protected function parseLayerData():void {
            _tileData = new Vector.<Vector.<TileInfo>>();
            _walkableData = new Vector.<Vector.<int>>();
            _tmpWalkableData = new Vector.<Vector.<int>>();

            var x:int;
            var y:int;
            var totalMapHeight:int = _associatedMap.totalHeightInTile;
            var totalMapWidth:int = _associatedMap.totalWidthInTile;
            var upperLimit:int = _associatedMap.upperLimit;
            var leftLimit:int = _associatedMap.leftLimit;
            var lowerLimit:int = _associatedMap.lowerLimit;
            var rightLimit:int = _associatedMap.rightLimit;

            if (_layerData == null) {
                // No layer data to parse, we have to built it from scratch
                for (y = 0; y < totalMapHeight; y++) {
                    if (y >= upperLimit && y < lowerLimit)
                        _tileData[y - upperLimit] = new Vector.<TileInfo>();
                    for (x = 0; x < totalMapWidth; x++) {
                        if (y >= upperLimit && y < lowerLimit && x >= leftLimit && x < rightLimit) {
                            _tileData[y - upperLimit][x - leftLimit] = null;
                        }
                    }
                }
            }
            else {
                if (_layerData.data[0] == null) {
                    Logger.error("Layer :: Layer <data> tag enclosed no data. (empty data tag)");
                }
                else {
                    _name = _layerData.@name;
                    var encodedValue:String = _layerData.data[0];
                    var decodedByteArray:ByteArray = Base64.decode(encodedValue);
                    var outputByteArray:ByteArray;
                    var tilesetID:int = -1;
                    var tileX:int = -1;
                    var tileY:int = -1;
                    var calculatedIndex:int;

                    decodedByteArray.uncompress();

                    outputByteArray = decodedByteArray;

                    for (y = 0; y < totalMapHeight; y++) {
                        if (y >= upperLimit && y < lowerLimit) {
                            _tileData[y - upperLimit] = new Vector.<TileInfo>();
                            _walkableData[y - upperLimit] = new Vector.<int>();
                            _tmpWalkableData[y - upperLimit] = new Vector.<int>();
                        }
                        for (x = 0; x < totalMapWidth; x++) {
                            tilesetID = 0;
                            tileX = 0;
                            tileY = 0;

                            calculatedIndex = y * totalMapWidth;
                            calculatedIndex += x;
                            calculatedIndex *= 6;

                            tilesetID |= (outputByteArray[calculatedIndex]);
                            tilesetID |= (outputByteArray[calculatedIndex + 1] << 8 );

                            tileX |= (outputByteArray[calculatedIndex + 2]);
                            tileX |= (outputByteArray[calculatedIndex + 3] << 8 );

                            tileY |= (outputByteArray[calculatedIndex + 4]);
                            tileY |= (outputByteArray[calculatedIndex + 5] << 8 );

                            if (y >= upperLimit && y < lowerLimit && x >= leftLimit && x < rightLimit) {
                                if (tilesetID == 0 || tileX < 0 || tileY < 0) {
                                    _tileData[y - upperLimit][x - leftLimit] = null;
                                }
                                else {
                                    var tileset:ITileset = _associatedMap.getTileset(tilesetID);
                                    if (tileset != null)
                                        _tileData[y - upperLimit][x - leftLimit] = new TileInfo(tileset, tileX, tileY);
                                    else
                                        _tileData[y - upperLimit][x - leftLimit] = null;
                                }
                                if (tileX == 65535)
                                    _walkableData[y - upperLimit][x - leftLimit] = -1;
                                else {
                                    _walkableData[y - upperLimit][x - leftLimit] = tileX;
                                }
                                _tmpWalkableData[y - upperLimit][x - leftLimit] = 1;
                            }
                        }
                    }
                }
            }
        }

        //----------------------------------
        // shift
        //----------------------------------

        public override function shift(xDir:int, yDir:int):void {
            super.shift(xDir, yDir);
            var mapHeight:int = _associatedMap.heightInTile;
            var mapWidth:int = _associatedMap.widthInTile + xDir;
            var x:int;
            var y:int;

            for (y = 0; y < mapHeight; y++) {
                for (x = 0; x < xDir; x++) {
                    _walkableData[y].splice(0, 0, -2);
                }
            }

            for (y = 0; y < yDir; y++) {
                var newTileY:Vector.<int> = new Vector.<int>();
                for (x = 0; x < mapWidth; x++) {
                    newTileY[x] = -2;
                }
                _walkableData.splice(0, 0, newTileY);
            }
        }

        //----------------------------------
        // append
        //----------------------------------

        public override function append(xDir:int, yDir:int):void {
            super.append(xDir, yDir);
            var mapHeight:int = _associatedMap.heightInTile;
            var mapWidth:int = _associatedMap.widthInTile + xDir;
            var x:int;
            var y:int;

            for (y = 0; y < mapHeight; y++) {
                for (x = 0; x < xDir; x++) {
                    _walkableData[y].push(-2);
                }
            }

            for (y = 0; y < yDir; y++) {
                var newTileY:Vector.<int> = new Vector.<int>();
                for (x = 0; x < mapWidth; x++) {
                    newTileY[x] = -2;
                }
                _walkableData.push(newTileY);
            }
        }

        //----------------------------------
        // isWalkable
        //----------------------------------

        public function isWalkable(x:int, y:int):Boolean {
            if (y < 0 || y >= _walkableData.length)
                return false;
            if (x < 0 || x >= _walkableData[0].length)
                return false;
            return _walkableData[y][x] != 0 && _tmpWalkableData[y][x] != 0;
        }

        //----------------------------------
        // isFilled
        //----------------------------------

        public function isFilled(posX:int, posY:int, width:int, height:int):Boolean {
            var isFilled:Boolean = true;
            var mapHeight:int = _associatedMap.heightInTile;
            var mapWidth:int = _associatedMap.widthInTile;
            var testHeight:int = posY + height;
            var testWidth:int = posX + width;
            var x:int;
            var y:int;
            // First we need to see if we overlap completely or not
            if (posX < 0 || posY < 0)
                return false;
            if (testWidth > mapWidth || testHeight > mapHeight)
                return false;

            // In case we overlap completely then we need to check
            posX = (posX < 0) ? 0 : posX;
            posY = (posY < 0) ? 0 : posY;
            testWidth = (testWidth < 0) ? 0 : testWidth;
            testHeight = (testHeight < 0) ? 0 : testHeight;

            for (y = posY; y < testHeight && y < mapHeight; y++) {
                for (x = posX; x < testWidth && x < mapWidth; x++) {
                    if (_walkableData[y][x] == -2) {
                        isFilled = false;
                        break;
                    }
                }
                if (!isFilled)
                    break;
            }
            return isFilled;
        }

        public function clearTempWalkable():void {
            _tmpWalkableData = new Vector.<Vector.<int>>();
            var x:int;
            var y:int;
            var totalMapHeight:int = _associatedMap.totalHeightInTile;
            var totalMapWidth:int = _associatedMap.totalWidthInTile;
            var upperLimit:int = _associatedMap.upperLimit;
            var leftLimit:int = _associatedMap.leftLimit;
            var lowerLimit:int = _associatedMap.lowerLimit;
            var rightLimit:int = _associatedMap.rightLimit;
            for (y = 0; y < totalMapHeight; y++) {
                if (y >= upperLimit && y < lowerLimit) {
                    _tmpWalkableData[y - upperLimit] = new Vector.<int>();
                    for (x = 0; x < totalMapWidth; x++) {
                        if (x >= leftLimit && x < rightLimit) {
                            _tmpWalkableData[y - upperLimit][x - leftLimit] = 1;
                        }
                    }
                }

            }
        }


        public function addTempUnwalkable(turnStartPos:IntPoint):void {
            _tmpWalkableData[turnStartPos.y][turnStartPos.x] = 0;
        }
    }
}